package org.rain.common.util.invoke;

import org.rain.common.util.Strs;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * created by yangtong on 2025/4/8 16:06:36
 * <br/>
 * 反射工具类
 */
public class Reflects {

    //缓存类的字段
    private static final Map<Class<?>, Field[]> fieldCache = new ConcurrentHashMap<>();
    //缓存类的方法
    private static final Map<String, Map<String, Method>> methodCache = new ConcurrentHashMap<>();

    // 快速实例化对象
    public static <T> T newInstance(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 获取类字段
    public static Field[] getFields(Class<?> clazz) {
        return fieldCache.computeIfAbsent(clazz, Class::getDeclaredFields);
    }

    // 获取指定字段
    private static Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
        for (Field field : getFields(clazz)) {
            if (field.getName().equals(name)) {
                return field;
            }
        }
        throw new NoSuchFieldException("Field not found: " + name);
    }

    // 设置字段值，本质是调用字段setter方法
    public static void setFieldValue(Object target, String fieldName, Object value) {
        try {
            Field field = getField(target.getClass(), fieldName);
            //获取set方法
            String name = field.getName();
            String setter = "set" + Strs.firstCapitalize(name);
            //调用setter方法
            invokeMethod(target, setter, new Class[]{field.getType()}, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 获取字段值，本质是调用字段getter方法
    public static Object getFieldValue(Object target, String fieldName) {
        try {
            Field field = getField(target.getClass(), fieldName);
            //获取getter方法
            String name = field.getName();
            String getter = "get" + Strs.firstCapitalize(name);
            //调用getter方法
            return invokeMethod(target, getter, new Class[]{});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 获取类方法
    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
        String classKey = clazz.getName();
        StringBuilder methodKey = new StringBuilder(methodName);
        for (Class<?> param : params) {
            methodKey.append("::").append(param.getName());
        }
        //获取指定类的方法Map数据
        Map<String, Method> methodMap = methodCache.computeIfAbsent(classKey, key -> new ConcurrentHashMap<>());
        //从methodMap中获取指定方法
        return methodMap.computeIfAbsent(methodKey.toString(), key -> {
            Method method;
            try {
                method = clazz.getDeclaredMethod(methodName, params);
                return method;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
    }


    // 调用方法
    public static Object invokeMethod(Object target, String methodName, Class<?>[] paramTypes, Object... args) {
        try {
            Method method = getMethod(target.getClass(), methodName, paramTypes);
            return method.invoke(target, args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
